import Error



class Lise:
    """
    Base class of a LiSe definition (metadata set in memory)

    """

    def __init__(self, digiSubdir=None, templateId=None, metadata={}, workflow=[]):
        """Arguments:
        digiSubdir(string) -- Relative path to the digitised pictures directory from the XML 
                              LiSe file path
        templateId(string) -- Web template identifier of this LiSe definition to use in the final 
                              LiSe website generation
        metadata(LiseMetadata{}) -- Dict of language identifiers and LiseMetadata instances related 
                                    to this LiSe definition

        """
        self.digiSubdir = digiSubdir
        self.templateId = templateId
        self.metadata = metadata
        self.workflow = workflow

    def GetDigiSubdir(self):
        return self.digiSubdir
    def GetTemplateId(self):
        return self.templateId
    def GetWorkflow(self):
        return self.workflow
    def SetDigiSubdir(self, digiSubdir):
        self.digiSubdir = digiSubdir
    def SetTemplateId(self, templateId):
        self.templateId = templateId
    def SetWorkflow(self, workflow):
        self.workflow = workflow

    def toNameSpace(self, includeMetadata=False, metadataNameSpace=False):
        """Returns a dictionary with all the attributes of this instance.

        includeMetadata(boolean) -- Wether include the attribute self.metadata
        metadataNameSpace(boolean) -- Wether return the toNameSpace() method of the LiseMetadata
                                      instance dictionary of the self.metadata attribute.
                                      Only works if includeMetadata is True

        """
        nameSpace = {}
        nameSpace['digiSubdir'] = self.digiSubdir
        nameSpace['templateId'] = self.templateId
        if includeMetadata:
            if metadataNameSpace:
                nameSpace['metadata'] = {}
                for key, value in self.metadata.iteritems():
                    nameSpace['metadata'][key] = value.toNameSpace(True, True)
            else:
                nameSpace['metadata'] = self.metadata
        return nameSpace

    def GetWorkflowPlainList(self):
        workflow = []
        for task in self.workflow:
            workflow.extend(task.GetWorkflowPlainList())
        return workflow



class LiseMetadata:
    """Stores the metadata in a certain language of a LiSe definition."""

    def __init__(self, author='', title='', year='', owner='', index=[]):
        """Arguments:
        author(string) -- The author of the digitised document
        title(string) -- The title
        year(string) -- The year
        owner(string) -- Who let you to digitise the document
        index([]) -- A list of LiseChapter|Page|Interval instances, storing the index of the doc

        """
        self.author = author
        self.title = title
        self.year = year
        self.owner = owner
        self.index = index

    def toNameSpace(self, includeIndex=False, indexNameSpace=False):
        """Read the toNameSpace method comments of the Lise class.

        includeIndex(boolean) -- Wether include the attribute self.index
        indexNameSpace(boolean) -- Wether return the toNameSpace() method or the 
                                   instance list of the self.index attribute.
                                    Only works if includeIndex is True

        """
        nameSpace = {}
        nameSpace['author'] = self.author
        nameSpace['title'] = self.title
        nameSpace['year'] = self.year
        nameSpace['owner'] = self.owner
        if includeIndex:
            if indexNameSpace:
                nameSpace['index'] = [page.toNameSpace(True, True) for page in self.index]
            else:
                nameSpace['index'] = self.index
        return nameSpace

    def getNumberOfPages(self):
        """Returns the number of pages of the index of this LiSe"""
        return sum([page.getNumberOfPages() for page in self.index])

    def getNumberOfPictures(self):
        """Returns the number of pictures related to the index of this LiSe"""
        return sum([page.getNumberOfPictures() for page in self.index])

    def getIndexPlainList(self):
        """Returns the index metadata of this LiSe. The returned format is a plain list
        (without nested lists) of dictionaries. Each dict is basicly the toNameSpace() 
        returned dict of each LiseChapter|Interval|Page of the index. It also includes
        'chapterEnd' labels to mark the end of a chapter, and the key 'tab' to show
        the nesting level of each page of the index.

        """
        index = []
        for page in self.index:
            index.extend(page.getIndexPlainList())
        return index

    def setIndexPlainList(self, indexPlainList):
        """getIndexPlainList() reverse method"""
        self.index = []
        i = 0
        while i < len(indexPlainList):
            if indexPlainList[i]['type'] == 'page':
                itemLise = LisePage()
            elif indexPlainList[i]['type'] == 'interval':
                itemLise = LiseInterval()
            elif indexPlainList[i]['type'] == 'chapter':
                itemLise = LiseChapter()
            i = itemLise.setIndexPlainList(indexPlainList, i)
            self.index.append(itemLise)

    def getIndexLeaves(self):
        """Returns the index metadata of this LiSe. The returned format is a plain list
        (without nested lists) of dictionaries. Each dict is basicly the toNameSpace() 
        returned dict of each LiseChapter|Interval|Page of the index. 
        It omits the chapter structure, it only returns the leaf pages.
        It also expands the LiseInterval instances into a list of LisePage instances.

        """
        index = []
        for page in self.index:
            index.extend(page.getIndexLeaves())
        return index

    def getIndexMenu(self, p):
        """Returns the index metadata of this LiSe. The returned format is a plain list
        (without nested lists) of dictionaries. Each dict is basicly the toNameSpace() 
        returned dict of each LiseChapter|Interval|Page of the index. 
        It only includes the content of the chapters where the specified page (p) is contained.
        It most cases, it returns a simplification of the entire index, focusing from the point
        of view of the specified page. For each page, includes the key 'tab' to show the nesting 
        level on the index.

        Arguments:
        p(int) -- The position of a page in the index

        """
        n = 0
        index = []
        for page in self.index:
            pageIndexMenu = page.getIndexMenu(p, n)
            index.extend(pageIndexMenu['index'])
            n = pageIndexMenu['n']
        return index



class LiseChapter:
    """Stores the information of a chapter of the index of this LiSe definition."""

    def __init__(self, chapterTitle='', index=[]):
        """Arguments:
        chaterTitle(string) -- The title of the chapter
        index([]) -- A list of LiseChapter|Page|Interval instances, storing the index of the 
                     chapter

        """
        self.chapterTitle = chapterTitle
        self.index = index

    def toNameSpace(self, includeIndex=False, indexNameSpace=False):
        """Read the toNameSpace method comments of the LiseMetadata class"""
        nameSpace = {}
        nameSpace['chapterTitle'] = self.chapterTitle
        if includeIndex:
            if indexNameSpace:
                nameSpace['index'] = [page.toNameSpace(True, True) for page in self.index]
            else:
                nameSpace['index'] = index
        return nameSpace

    def getNumberOfPages(self):
        """Read the getNumberOfPages method comments of the LiseMetadata class"""
        return sum([page.getNumberOfPages() for page in self.index])

    def getNumberOfPictures(self):
        """Read the getNumberOfPictures method comments of the LiseMetadata class"""
        return sum([page.getNumberOfPictures() for page in self.index])

    def getIndexPlainList(self, tab=0):
        """Read the getIndexPlainList method comments of the LiseMetadata class"""
        nameSpace = self.toNameSpace(False)
        nameSpace['type'] = 'chapter'
        nameSpace['tab'] = tab
        index = [nameSpace]
        for page in self.index:
            index.extend(page.getIndexPlainList(tab+1))
        index.extend([{'type':'chapterEnd', 'tab':tab}])
        return index

    def setIndexPlainList(self, indexPlainList, i):
        """Read the setIndexPlainList method comments of the LiseMetadata class"""
        self.chapterTitle = indexPlainList[i]['chapterTitle']
        self.index = []
        i += 1
        while i < len(indexPlainList):
            if indexPlainList[i]['type'] == 'page':
                itemLise = LisePage()
            elif indexPlainList[i]['type'] == 'interval':
                itemLise = LiseInterval()
            elif indexPlainList[i]['type'] == 'chapter':
                itemLise = LiseChapter()
            elif indexPlainList[i]['type'] == 'chapterEnd':
                i += 1
                break
            i = itemLise.setIndexPlainList(indexPlainList, i)
            self.index.append(itemLise)
        return i

    def getIndexLeaves(self):
        """Read the getIndexLeaves method comments of the LiseMetadata class"""
        index = []
        for page in self.index:
            index.extend(page.getIndexLeaves())
        if len(index) > 0:
            index[0]['pageTitle'] = self.chapterTitle + ' / ' + index[0]['pageTitle']
        return index

    def getIndexMenu(self, p, n, tab=0):
        """Read the getIndexMenu method comments of the LiseMetadata class"""
        numberOfPages = self.getNumberOfPages()
        nameSpace = self.toNameSpace()
        nameSpace['tab'] = tab
        nameSpace['n'] = n
        if (n <= p) and (p < n + numberOfPages):
            nameSpace['type'] = 'chapter_opened'
            index = [nameSpace]
            for page in self.index:
                pageIndexMenu = page.getIndexMenu(p, n, tab+1)
                index.extend(pageIndexMenu['index'])
                n = pageIndexMenu['n']
            return ({'index':index, 'n':n})
        else:
            nameSpace['type'] = 'chapter_closed'
            return ({'index':[nameSpace], 'n':n+numberOfPages})



class LiseInterval:
    """Stores the information of a set of pages of the index of this LiSe definition."""

    def __init__(self, from_=None, to_=None, prefix='', suffix=''):
        """Arguments:
        from_(int) -- The number of the first page of the interval of pages
                      ("from" is a reserved keyword, that's why the attribute is named from_)
        to_(int) -- The number of the last page of the interval of pages
                    ("to" is a reserved keyword, that's why the attribute is named to_)
        prefix(string) -- Prefix to add in the title of each page of the interval
        suffix(string) -- Suffix to add in the title of each page of the interval

        """
        self.from_ = from_
        self.to_ = to_
        self.prefix = prefix
        self.suffix = suffix

    def toNameSpace(self, includeIndex=None, indexNameSpace=None):
        """Read the toNameSpace method comments of the LiseMetadata class"""
        nameSpace = {}
        nameSpace['type'] = 'interval'
        nameSpace['from'] = self.from_
        nameSpace['to'] = self.to_
        nameSpace['prefix'] = self.prefix
        nameSpace['suffix'] = self.suffix
        return nameSpace

    def getNumberOfPages(self):
        """Read the getNumberOfPages method comments of the LiseMetadata class"""
        return (self.to_ - self.from_ + 1)

    def getNumberOfPictures(self):
        """Read the getNumberOfPictures method comments of the LiseMetadata class"""
        return (self.to_ - self.from_ + 1)

    def getIndexPlainList(self, tab=0):
        """Read the getIndexPlainList method comments of the LiseMetadata class"""
        nameSpace = self.toNameSpace()
        nameSpace['type'] = 'interval'
        nameSpace['tab'] = tab
        return [nameSpace]

    def setIndexPlainList(self, indexPlainList, i):
        """Read the setIndexPlainList method comments of the LiseMetadata class"""
        self.from_ = indexPlainList[i]['from']
        self.to_ = indexPlainList[i]['to']
        self.prefix = indexPlainList[i]['prefix']
        self.suffix = indexPlainList[i]['suffix']
        return i+1

    def getIndexLeaves(self):
        """Read the getIndexLeaves method comments of the LiseMetadata class"""
        index = []
        for i in range(self.from_, self.to_+1):
            lisePage = LisePage(self.prefix + str(i) + self.suffix)
            index.extend(lisePage.getIndexLeaves())
        return index

    def getIndexMenu(self, p, n, tab=0):
        """Read the getIndexMenu method comments of the LiseMetadata class"""
        index = []
        for i in range(self.from_, self.to_+1):
            lisePage = LisePage(self.prefix + str(i) + self.suffix)
            nameSpace = lisePage.toNameSpace()
            nameSpace['tab'] = tab
            nPage = n + i - self.from_
            nameSpace['n'] = nPage
            if (p == nPage):
                nameSpace['selected'] = True
            index.append(nameSpace)
        return ({'index':index, 'n':n+self.getNumberOfPages()})



class LisePage:
    """Stores the information of a page of the index of this LiSe definition."""

    def __init__(self, pageTitle='', picturesPerPage=1, notes=None, notesStyle=None):
        """Arguments:
        pageTitle(string) -- The title of the page
        picturesPerPage(int) -- The amount of digitised pages related to this entry of the index
        notes(string) -- Notes related to this entry of the index
        notesStyle(int) -- Css style attributes of the previous notes

        """
        self.pageTitle = pageTitle
        self.picturesPerPage = picturesPerPage
        self.notes = notes
        self.notesStyle = notesStyle

    def toNameSpace(self, includeIndex=None, indexNameSpace=None):
        """Read the toNameSpace method comments of the LiseMetadata class"""
        nameSpace = {}
        nameSpace['type'] = 'page'
        nameSpace['pageTitle'] = self.pageTitle
        nameSpace['picturesPerPage'] = self.picturesPerPage
        nameSpace['notes'] = self.notes
        nameSpace['notesStyle'] = self.notesStyle
        return nameSpace

    def getNumberOfPages(self):
        """Read the getNumberOfPages method comments of the LiseMetadata class"""
        return 1

    def getNumberOfPictures(self):
        """Read the getNumberOfPictures method comments of the LiseMetadata class"""
        return self.picturesPerPage

    def getIndexPlainList(self, tab=0):
        """Read the getIndexPlainList method comments of the LiseMetadata class"""
        nameSpace = self.toNameSpace()
        nameSpace['type'] = 'page'
        nameSpace['tab'] = tab
        return [nameSpace]

    def setIndexPlainList(self, indexPlainList, i):
        """Read the setIndexPlainList method comments of the LiseMetadata class"""
        self.pageTitle = indexPlainList[i]['pageTitle']
        self.picturesPerPage = indexPlainList[i]['picturesPerPage']
        self.notes = indexPlainList[i]['notes']
        self.notesStyle = indexPlainList[i]['notesStyle']
        return i+1

    def getIndexLeaves(self):
        """Read the getIndexLeaves method comments of the LiseMetadata class"""
        nameSpace = self.toNameSpace()
        return [nameSpace]

    def getIndexMenu(self, p, n, tab=0):
        """Read the getIndexMenu method comments of the LiseMetadata class"""
        nameSpace = self.toNameSpace()
        nameSpace['tab'] = tab
        nameSpace['n'] = n
        if (p == n):
            nameSpace['selected'] = True
        return ({'index':[nameSpace], 'n':n+self.getNumberOfPages()})



class LiseWorkflow:

    def __init__(self, name, checked=False, tasks=[], notes=''):
        self.name = name
        self.checked = checked
        self.tasks = tasks
        self.notes = notes

    def toNameSpace(self, includeSubtasks=True):
        nameSpace = {}
        nameSpace['name'] = self.name
        nameSpace['checked'] = self.checked
        if includeSubtasks:
            nameSpace['tasks'] = [i.toNameSpace() for i in self.tasks]
        nameSpace['notes'] = self.notes
        return nameSpace

    def GetWorkflowPlainList(self, tab=0):
        """Read the getIndexPlainList method comments of the LiseMetadata class"""
        nameSpace = self.toNameSpace(False)
        nameSpace['type'] = 'task'
        nameSpace['tab'] = tab
        tasks = [nameSpace]
        for task in self.tasks:
            tasks.extend(task.GetWorkflowPlainList(tab+1))
        tasks.extend([{'type':'taskEnd', 'tab':tab}])
        return tasks


